package formatter;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import optimizer.Instruction;
import optimizer.Register;

public class AssemblyFileReader {
	private String filename;
	private BufferedReader reader;

	public AssemblyFileReader(String fileNameWithPath){
		this.filename = fileNameWithPath;
		try {
			this.reader = new BufferedReader(new FileReader(new File(this.filename)));
		} catch (FileNotFoundException e) {
			System.out.println("AssemblyFileReader.AssemblyFileReader() : Specified file " + this.filename + " does not exists.");
		}
	}

	public Instruction readInstruction() throws IOException{		
		String sCurrentLine = null;	
		String sOriginal = null;
		int index = -1;
		int index2 = -1;

		String label = null; 
		String op=null;
		Register writtenTo=null;
		Register readFrom1=null;
		Register readFrom2=null;
		int constant=-1;
		String branchTarget=null;

		// After the label there should be a colon (':') exits before the operation
		while(((sCurrentLine = reader.readLine()) != null )&& sCurrentLine.isEmpty()){
			
		}
		if (sCurrentLine != null) {
			sCurrentLine.trim();
			sOriginal = sCurrentLine;

			// Extract the Label  if exists
			index = sCurrentLine.indexOf(":");
			if(index >= 0) { 
				label = sCurrentLine.substring(0, index).trim();
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();
			}

			// Extract the Operation
			index = sCurrentLine.indexOf(" ");
			if(sCurrentLine.isEmpty()) {
				System.out.println("AssemblyFileReader.readInstruction(): Error, operation is empty");
				throw new IOException();
			}
			if(index < 0)
				op = sCurrentLine;
			else
				op = sCurrentLine.substring(0, index).trim();
			sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

			// Rest of the arguments depends on the operations


			if("LI".equalsIgnoreCase(op)) {
				// get the next register
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the integer which in last place
				sCurrentLine = sCurrentLine.substring(1, sCurrentLine.length()).trim();
				constant = Integer.parseInt(sCurrentLine);

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("LT".equalsIgnoreCase(op)) {
				// get the next register writtenTo
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the next register readFrom1
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the last register readFrom2
				readFrom2 = Register.getRegister(sCurrentLine);

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("LW".equalsIgnoreCase(op)) {
				// get the next register writtenTo
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the next register readFrom1
				index = sCurrentLine.indexOf("[");
				index2 = sCurrentLine.indexOf("]");
				if(index<=0 && index2<0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(index+1, index2).trim());

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("SW".equalsIgnoreCase(op)) {
				// get the next register writtenTo
				index = sCurrentLine.indexOf("[");
				index2 = sCurrentLine.indexOf("]");
				if(index < 0 || index2 < 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(index+1, index2).trim());
				sCurrentLine = sCurrentLine.substring(index2+1, sCurrentLine.length()).trim();

				// get the next register readFrom1
				index = sCurrentLine.indexOf(",");
				if(index<0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(index+1, sCurrentLine.length()).trim());

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("ADD".equalsIgnoreCase(op)) {
				// get the next register writtenTo
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the next register readFrom1
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the last register readFrom2
				readFrom2 = Register.getRegister(sCurrentLine);

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("SUB".equalsIgnoreCase(op)) {
				// get the next register writtenTo
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, writtenTo register is empty");
					throw new IOException();
				}
				writtenTo = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the next register readFrom1
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the last register readFrom2
				readFrom2 = Register.getRegister(sCurrentLine);

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("BEQZ".equalsIgnoreCase(op)) {
				// get the next register readFrom1
				index = sCurrentLine.indexOf(",");
				if(index <= 0) {
					System.out.println("AssemblyFileReader.readInstruction(): Error, readFrom1 register is empty");
					throw new IOException();
				}
				readFrom1 = Register.getRegister(sCurrentLine.substring(0, index).trim());
				sCurrentLine = sCurrentLine.substring(index+1, sCurrentLine.length()).trim();

				// get the label of branch target
				branchTarget =sCurrentLine;

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("GOTO".equalsIgnoreCase(op)) {
				// get the label of branch target
				branchTarget =sCurrentLine;

				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("DATA".equalsIgnoreCase(op)) {
				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("IN".equalsIgnoreCase(op)) {
				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("OUt".equalsIgnoreCase(op)) {
				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
			else if("HALT".equalsIgnoreCase(op)) {
				System.out.println("AssemblyFileReader.readInstruction():'" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
				return new Instruction(label,op,writtenTo,readFrom1,readFrom2,constant,branchTarget, sOriginal);
			}
		}
		else {
			//System.out.println("AssemblyFileReader.readInstruction(): Error - null instruction '" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
			return null;
		}
		System.out.println("AssemblyFileReader.readInstruction(): Error - incomplete instruction '" + label + "' : '" + op + "' " + writtenTo + ", " + readFrom1 + ", " + readFrom2 + ", " + constant + ", '" + branchTarget + "' ");
		return null;
	}

	public void closeFile(){
		if (reader != null)
			try {
				reader.close();
			} catch (IOException e) {
				System.out.println("AssemblyFileReader.closeFile(): Error while closing the file - " + this.filename);
			}
	}
}
